package com.ia.aistream.manager.authen.impl;

import com.google.common.collect.Maps;
import com.ia.aistream.common.exception.base.CustomException;
import com.ia.aistream.common.utils.TimeUtils;
import com.ia.aistream.manager.authen.ITokenManager;
import com.ia.aistream.manager.authen.conf.AuthenConf;
import com.ia.aistream.manager.authen.conf.TokenFrom;
import com.ia.aistream.manager.framework.redis.RedisKey;
import com.ia.aistream.manager.framework.redis.RedisManager;
import com.ia.aistream.model.system.dto.OpenTokenDto;
import com.ia.aistream.model.system.entity.SysUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultClaims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * @author guoxy
 */
@Slf4j
@Service
public class TokenManagerImpl implements ITokenManager {
    @Autowired
    private AuthenConf authenConf;
    @Autowired
    private RedisManager redisManager;


    @Override
    public void saveToken(TokenFrom from, String loginTokenId, SysUser user) {
        // 保存tokenId与admin对应关心信息到redis中
        String key = from.getRedisKey(loginTokenId);
        if (StringUtils.isBlank(key)) {
            return;
        }
        user.setPassword(null);
        int expire = authenConf.defaultTokenExpires(from);
        redisManager.setex(RedisKey.authen_token_open_string.getKey(user.getUserId()), expire, user);
        redisManager.setex(from.getRedisKey(loginTokenId), expire, user.getUserId());
    }

    @Override
    public void deleteToken(String token) {
        AuthenConf.Token tokenConf = authenConf.getToken();
        Claims claims = Jwts.parser().requireIssuer(tokenConf.getIssuer()).setSigningKey(tokenConf.getSecret()).parseClaimsJws(token).getBody();
        String kind = claims.get(ADMIN_TOKEN_KIND_KEY, String.class);
        TokenFrom from = TokenFrom.claimsKindOf(kind);
        String tokenId;
        tokenId = claims.get(ADMIN_TOKEN_KIND_KEY, String.class);

        redisManager.del(from.getRedisKey(tokenId));
    }

    @Override
    public OpenTokenDto generateUserToken(TokenFrom from, String tokenId) {
        String redisKey = from.getRedisKey(tokenId);
        Integer userId = redisManager.get(redisKey, Integer.class);
        if (userId == null) {
            throw new CustomException("登录处理错误，请重试");
        }

        SysUser user = redisManager.get(RedisKey.authen_token_open_string.getKey(userId), SysUser.class);
        if (user == null) {
            throw new CustomException("登录处理错误，请重试");
        }

        int expire = authenConf.defaultTokenExpires(from);
        AuthenConf.Token tokenConf = authenConf.getToken();
        Map<String, Object> map = Maps.newHashMap();
        map.put(ADMIN_TOKENID_KEY, tokenId);
        map.put(ADMIN_TOKEN_KIND_KEY, from.getClaimsKind());
        map.put("userId", user.getUserId());
        map.put("login", TimeUtils.getUnixTime(System.currentTimeMillis()));
        // token 本身不设置过期时间，token的过期时间只看redis
        map.put("exp", System.currentTimeMillis() / 1000 + 315360000);
        map.put("iss", tokenConf.getIssuer());

        Claims claims = new DefaultClaims(map).setIssuer(tokenConf.getIssuer());
        String token = Jwts.builder().setIssuer(tokenConf.getIssuer()).signWith(SignatureAlgorithm.HS256, tokenConf.getSecret()).setClaims(claims).compact();
        return new OpenTokenDto(token, expire);

    }

    @Override
    public SysUser getTokenUser(String adminToken) {
        AuthenConf.Token tokenConf = authenConf.getToken();
        Claims claims;
        try {
            claims = Jwts.parser().requireIssuer(tokenConf.getIssuer()).setSigningKey(tokenConf.getSecret()).parseClaimsJws(adminToken).getBody();
        } catch (ExpiredJwtException e) {
            log.error("[ jwt ] adminToken 验证异常:", e);
            return null;
        }

        String claimsKind = claims.get(ADMIN_TOKEN_KIND_KEY, String.class);
        String tokenId = claims.get(ADMIN_TOKENID_KEY, String.class);
        TokenFrom from = TokenFrom.claimsKindOf(claimsKind);
        if (StringUtils.isBlank(tokenId)) {
            return null;
        }

        this.refresh(from, tokenId);

        String redisKey = from.getRedisKey(tokenId);
        String adminId = redisManager.get(redisKey, String.class);
        return redisManager.get(RedisKey.authen_token_open_string.getKey(adminId), SysUser.class);
    }

    @Override
    public void refreshUserToken(String userToken) {
        if (StringUtils.isBlank(userToken)) {
            return;
        }

        AuthenConf.Token tokenConf = authenConf.getToken();
        Claims claims;
        try {
            claims = Jwts.parser().requireIssuer(tokenConf.getIssuer()).setSigningKey(tokenConf.getSecret()).parseClaimsJws(userToken).getBody();
        } catch (ExpiredJwtException e) {
            log.error("[ jwt ] adminToken 验证异常：{}", e.getMessage());
            return;
        }

        String claimsKind = claims.get(ADMIN_TOKEN_KIND_KEY, String.class);
        TokenFrom from = TokenFrom.claimsKindOf(claimsKind);
        String tokenId = claims.get(ADMIN_TOKENID_KEY, String.class);
        if (StringUtils.isBlank(tokenId)) {
            return;
        }
        this.refresh(from, tokenId);
    }

    /**
     * 更新token有效时长
     *
     * @param from
     * @param tokenId
     */
    private void refresh(TokenFrom from, String tokenId) {
        String tokenRedisKey = from.getRedisKey(tokenId);
        Integer userId = redisManager.get(tokenRedisKey, Integer.class);
        if (userId == null) {
            return;
        }

        int expire = authenConf.defaultTokenExpires(from);
        redisManager.expire(tokenRedisKey, expire);
        redisManager.expire(RedisKey.authen_token_open_string.getKey(userId), expire);
    }
}
